#include "stdafx.h"
#include "log/eventlogchannel.hpp"
#include "log/jhmsg.hpp"
#include "charset/string.hpp"
using namespace JHCPP::charset;

namespace JHCPP
{
	namespace log
	{
		const std::string CEventLogChannel::PROP_NAME    = "name";
		const std::string CEventLogChannel::PROP_HOST    = "host";
		const std::string CEventLogChannel::PROP_LOGHOST = "loghost";
		const std::string CEventLogChannel::PROP_LOGFILE = "logfile";

		CEventLogChannel::CEventLogChannel() : m_logFile("Application"), m_h(0)
		{
			const DWORD maxPathLen = MAX_PATH + 1;

			char name[maxPathLen];
			int n = ::GetModuleFileName(NULL, name, maxPathLen);
			if (n > 0)
			{
				char* end = name + n - 1;
				while (end > name && *end != '\\') --end;
				if (*end == '\\') ++end;
				m_name = end;
			}
		}

		CEventLogChannel::CEventLogChannel(const std::string& name) : m_name(name), m_logFile("Application"), m_h(0)
		{
		}

		CEventLogChannel::CEventLogChannel(const std::string& name, const std::string& host) 
		: m_name(name), 
		m_host(host), 
		m_logFile("Application"), 
		m_h(0)
		{
		}

		CEventLogChannel::~CEventLogChannel()
		{
			close();
		}

		void CEventLogChannel::open()
		{
			setUpRegistry();

			m_h = ::RegisterEventSource(m_host.empty() ? NULL : m_host.c_str(), m_name.c_str());

			if (!m_h) throw SystemException("cannot register event source");
		}

		void CEventLogChannel::close()
		{
			if (m_h) ::DeregisterEventSource(m_h);
			m_h = 0;
		}

		void CEventLogChannel::log(const CMessage& msg)
		{
			if (!m_h) open();

			const char* pMsg = msg.getText().c_str();
			::ReportEvent(m_h, getType(msg), getCategory(msg), JH_MSG_LOG, NULL, 1, 0, &pMsg, NULL); 
		}

		void CEventLogChannel::setProperty(const std::string& name, const std::string& value)
		{
			if (icompare(name, PROP_NAME) == 0)
				m_name = value;
			else if (icompare(name, PROP_HOST) == 0)
				m_host = value;
			else if (icompare(name, PROP_LOGHOST) == 0)
				m_host = value;
			else if (icompare(name, PROP_LOGFILE) == 0)
				m_logFile = value;
			else
				CChannel::setProperty(name, value);
		}

		std::string CEventLogChannel::getProperty(const std::string& name) const
		{
			if (icompare(name, PROP_NAME) == 0)
				return m_name;
			else if (icompare(name, PROP_HOST) == 0)
				return m_host;
			else if (icompare(name, PROP_LOGHOST) == 0)
				return m_host;
			else if (icompare(name, PROP_LOGFILE) == 0)
				return m_logFile;
			else
				return CChannel::getProperty(name);
		}

		int CEventLogChannel::getType(const CMessage& msg)
		{
			switch (msg.getPriority())
			{
			case CMessage::PRIO_TRACE:
			case CMessage::PRIO_DEBUG:
			case CMessage::PRIO_INFORMATION:
				return EVENTLOG_INFORMATION_TYPE;
			case CMessage::PRIO_NOTICE:
			case CMessage::PRIO_WARNING:
				return EVENTLOG_WARNING_TYPE;
			default:
				return EVENTLOG_ERROR_TYPE;
			}
		}

		int CEventLogChannel::getCategory(const CMessage& msg)
		{
			switch (msg.getPriority())
			{
			case CMessage::PRIO_TRACE:
				return JH_CTG_TRACE;
			case CMessage::PRIO_DEBUG:
				return JH_CTG_DEBUG;
			case CMessage::PRIO_INFORMATION:
				return JH_CTG_INFORMATION;
			case CMessage::PRIO_NOTICE:
				return JH_CTG_NOTICE;
			case CMessage::PRIO_WARNING:
				return JH_CTG_WARNING;
			case CMessage::PRIO_ERROR:
				return JH_CTG_ERROR;
			case CMessage::PRIO_CRITICAL:
				return JH_CTG_CRITICAL;
			case CMessage::PRIO_FATAL:
				return JH_CTG_FATAL;
			default:
				return 0;
			}
		}

		void CEventLogChannel::setUpRegistry() const
		{
			std::string key = "SYSTEM\\CurrentControlSet\\Services\\EventLog\\";
			key.append(m_logFile);
			key.append("\\");
			key.append(m_name);
			HKEY hKey;
			DWORD disp;

			DWORD rc = ::RegCreateKeyEx(HKEY_LOCAL_MACHINE, key.c_str(), 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, &disp);

			if (rc != ERROR_SUCCESS) return;

			if (disp == REG_CREATED_NEW_KEY)
			{
				std::string path = "jhMsg";

				if (!path.empty())
				{
					DWORD count = 8;
					DWORD types = 7;

					::RegSetValueEx(hKey, "CategoryMessageFile", 0, REG_SZ, (const BYTE*) path.c_str(), static_cast<DWORD>(path.size() + 1));
					::RegSetValueEx(hKey, "EventMessageFile", 0, REG_SZ, (const BYTE*) path.c_str(), static_cast<DWORD>(path.size() + 1));
					::RegSetValueEx(hKey, "CategoryCount", 0, REG_DWORD, (const BYTE*) &count, static_cast<DWORD>(sizeof(count)));
					::RegSetValueEx(hKey, "TypesSupported", 0, REG_DWORD, (const BYTE*) &types, static_cast<DWORD>(sizeof(types)));
				}
			}
			::RegCloseKey(hKey);
		}

		std::string CEventLogChannel::findLibrary(const char* name)
		{
			std::string path;
			HMODULE dll = ::LoadLibrary(name);
			if (dll)
			{
				const DWORD maxPathLen = MAX_PATH + 1;
				char name[maxPathLen];
				int n = ::GetModuleFileName(dll, name, maxPathLen);
				if (n > 0) path = name;
				::FreeLibrary(dll);
			}
			return path;
		}

	}//end of namespace log
}//end of namespace JHCPP
